!function ($) {
    /**
     * Example for use:
                <span data-widget="modal" data-target=".test-widget-modal">click</span>
                <div class="test-widget-modal hidden">content content</div>
     */
    "use strict";

    var
        Modal,
        CurrentModal = null,
        CLASS_MODAL = 'widget-modal',
        CLASS_CLOSE = CLASS_MODAL + '__close',
        CLASS_BODY = CLASS_MODAL + '__body',
        CLASS_BACKDROP = CLASS_MODAL + '__backdrop',
        CLASS_ANIMATION_IN = 'animate-in',
        TEMPLATE_MODAL = '<div class="' + CLASS_MODAL + '" tabindex="-1">' +
            '<div class="' + CLASS_CLOSE + '" data-dismiss="modal">&#10006;</div>' +
            '<div class="' + CLASS_BODY + '"></div>' +
            '</div>';

    Modal = function (element, options) {
        this.options = options
        this.$element = $(element);

        this.$target = $();

        this.$element.on('forceHide.modal', this.hide.bind(this));
    };


    Modal.prototype = {
        constructor: Modal,

        toggle: function () {
            return this[!this.isShown ? 'show' : 'hide']();
        },

        show: function () {
            var e = $.Event('show');

            this.$element.trigger(e);

            if (CurrentModal) {
                CurrentModal.toggle();
            }

            CurrentModal = this;

            if (this.isShown || e.isDefaultPrevented()) {
                return;
            }

            this.isShown = true;

            if (!this.$template) {
                this.$template = $(TEMPLATE_MODAL);
                this.$template.appendTo(document.body);
                this.$element.appendTo(this.$template.find('.' + CLASS_BODY));
                this.$element.show();
//                this.$element = this.$template;
                this.$template.on('click.dismiss.modal', '[data-dismiss="modal"]', this.hide.bind(this));
            }

            this.escape();

            this.backdrop();

//            if (!this.$template.parent().length) {
//                this.options.width && this.$template.width(this.options.width);
//                this.$template.appendTo(document.body) //don't move modals dom position
//            }

            this.$element.show();
            this.$template.show();
            this.enforceFocus();
            this.$template.focus();
            this.$element.trigger('shown');

            this.updatePosition();
        },

        enforceFocus: function () {
            $(document).on('focusin.modal', function (e) {
                if (this.$template[0] !== e.target && !this.$template.has(e.target).length) {
                    this.$template.focus();
                }
            }.bind(this));
        },

        escape: function () {
            if (this.isShown && this.options.keyboard) {
                this.$template.on('keyup.dismiss.modal', function (e) {
                    e.which == 27 && this.hide();
                }.bind(this));
            } else if (!this.isShown) {
                this.$template.off('keyup.dismiss.modal');
            }
        },

        backdrop: function (callback) {
            if (this.isShown) {
                this.$backdrop = $('<div class="' + CLASS_BACKDROP + '" />').appendTo(document.body)

                this.$backdrop.click(this.hide.bind(this));

                this.$backdrop.addClass(CLASS_ANIMATION_IN);

            } else if (!this.isShown && this.$backdrop) {
                this.$backdrop.removeClass(CLASS_ANIMATION_IN);
            }

            $.isFunction(callback) && callback();
        },

        hide: function (e) {
            e && e.preventDefault();

            e = $.Event('hide');

            this.$element.trigger(e);

            CurrentModal = null;

            if (!this.isShown || e.isDefaultPrevented()) {
                return;
            }

            this.isShown = false;

            this.escape();

            $(document).off('focusin.modal');

            this.$template.hide();

            this.backdrop();
            this.removeBackdrop();
            this.$element.trigger('hidden');
        },

        removeBackdrop: function () {
            this.$backdrop && this.$backdrop.remove();
            this.$backdrop = null;
        },

        updatePosition: function () {
            if (this.isShown) {
                var $window = $(window),
                    left,
                    top = ($window.height() - this.$template.height()) / 2 + $window.scrollTop();
                if (top < 0) {
                    top = 0;
                }
                this.$template.css('top', top + "px");
                if (this.options.width) {
                    left = ($window.height() - this.$template.height()) / 2 + $window.scrollLeft();
                    if (left < 0) {
                        left = 0;
                    }
                    this.$template.css({
                        'margin-left': 0,
                        'left': (left) + 'px'
                    });
                }
                else {
                    this.$template.css('margin-left', -(this.$template.outerWidth() / 2) + "px");
                }
            }
        },

        setTarget: function ($target) {
            this.$target = $target;
        }
    };

    $.fn.modal = function (option, $target) {
        return this.each(function () {
            var $this = $(this),
                data = $this.data('modal'),
                options = $.extend({}, $.fn.modal.defaults, $this.data(), typeof option == 'object' && option);

            if (!data) {
                $this.data('modal', (data = new Modal(this, options, $target)))
            }
            if ($target) {
                data.setTarget($target);
            }
            if (typeof option == 'string') {
                data[option]();
            }
            else if (options.show) {
                data.show();
            }
        });
    };

    $.fn.modal.defaults = {
        keyboard: true,
        show: true
    };

    // Base init
    $(document).on('click.modal.widgets', '[data-widget="modal"]', function (e) {
        var $this = $(this),
            $target = $($this.attr('data-target')),
            option = $target.data('modal') ? 'toggle' : $.extend({}, $target.data(), $this.data());

        e.preventDefault();

        $target
            .modal(option, $this)
            .one('hide', function () {
                $this.focus()
            });
    });

}(window.jQuery);
